package org.grayrabbit.cms.plugin.loaders;

import java.io.File;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.grayrabbit.cms.plugin.ModuleDescriptorFactory;
import org.grayrabbit.cms.plugin.Plugin;
import org.grayrabbit.cms.plugin.PluginException;
import org.grayrabbit.cms.plugin.PluginParseException;
import org.grayrabbit.cms.plugin.loaders.classloading.DeploymentUnit;
import org.grayrabbit.cms.plugin.loaders.classloading.ScanFile;
import org.grayrabbit.cms.plugin.parsers.DescriptorParser;
import org.grayrabbit.cms.plugin.parsers.DescriptorParserFactory;
import org.grayrabbit.cms.plugin.parsers.XmlDescriptorParserFactory;
import org.grayrabbit.cms.utils.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class ClassLoadingPluginLoader implements PluginLoader {
	 private static Logger log = LoggerFactory.getLogger(ClassLoadingPluginLoader.class);
	 private final String pluginDescriptorFileName;
	 private final ScanFile scanner;
	 private final PluginFactory pluginFactory;
	 private final DescriptorParserFactory descriptorParserFactory;

	private Map<DeploymentUnit, Plugin> plugins;
	 
	public ClassLoadingPluginLoader(File path,PluginFactory pluginFactory) {
		 this(path, "cms-plugin.xml",pluginFactory);
	}

	public ClassLoadingPluginLoader(File path, String pluginDescriptorFileName,PluginFactory pluginFactory) {
		log.debug("Creating classloader for url {}",path);
	    this.scanner = new ScanFile(path);
	    this.pluginDescriptorFileName = pluginDescriptorFileName;
	    this.plugins = new HashMap<DeploymentUnit,Plugin>();
	    this.pluginFactory = pluginFactory;
	    descriptorParserFactory =  new XmlDescriptorParserFactory();
	}

	public Collection<Plugin> loadAllPlugins(ModuleDescriptorFactory moduleDescriptorFactory) {
		this.scanner.scan();
		for(DeploymentUnit deploymentUnit : this.scanner.getDeploymentUnits()){
			 try
		      {
		        Plugin plugin = deployPluginFromUnit(deploymentUnit, moduleDescriptorFactory);
		        this.plugins.put(deploymentUnit, plugin);
		      }
		      catch (PluginParseException e)
		      {
		        log.error("Error loading descriptor for : " + deploymentUnit, e);
		      }
		}
		return this.plugins.values();
	}

	private Plugin deployPluginFromUnit(DeploymentUnit deploymentUnit,ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException {
			Plugin plugin = null;
		    InputStream pluginDescriptor = null;
		    PluginClassLoader loader = new PluginClassLoader(deploymentUnit.getPath(), Thread.currentThread().getContextClassLoader());
		    try
		    {
		      if (loader.getResource(this.pluginDescriptorFileName) == null) {
		        throw new PluginParseException("No descriptor found in classloader for : " + deploymentUnit);
		      }
		      pluginDescriptor = loader.getResourceAsStream(this.pluginDescriptorFileName);

		      DescriptorParser parser = this.descriptorParserFactory.getInstance(pluginDescriptor);
		      plugin = parser.configurePlugin(moduleDescriptorFactory, this.pluginFactory.createPlugin(deploymentUnit, loader));
		    }
		    catch (PluginParseException e)
		    {
		      loader.close();
		      throw e;
		    }
		    catch (RuntimeException e)
		    {
		      loader.close();
		      throw e;
		    }
		    catch (Error e)
		    {
		      loader.close();
		      throw e;
		    }
		    finally
		    {
		      FileUtils.shutdownStream(pluginDescriptor);
		    }
		    return plugin;
	}

	public void removePlugin(Plugin plugin) throws PluginException {
		// TODO Auto-generated method stub

	}

	public boolean supportsAddition() {
		// TODO Auto-generated method stub
		return false;
	}

	public boolean supportsRemoval() {
		// TODO Auto-generated method stub
		return false;
	}


}
